package demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import demo.common.ResultJson;
import demo.jwt.AuthTokenFilter;
import demo.jwt.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * spring security 安全性配置
 */
@Configuration
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        prePostEnabled = true)
public class SecurityConfig {
    // 默认的安全类型，session 或 jwt
    @Value("${security.type:jwt}")
    private String securityType;
    // 密码加密工具
    private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors().and().
                csrf().disable() // 不使用 CRSF
                .authorizeRequests()
                .antMatchers("/student/**").authenticated() // /security 必须登陆才能访问
                .anyRequest().permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -> {
                    // 用户未登录，访问必须登录才能访问的url
                    ResultJson result = ResultJson.error(HttpStatus.UNAUTHORIZED).message(authException.getMessage());
                    writeResult(response, HttpStatus.UNAUTHORIZED, result);
                })
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    // 没有权限
                    ResultJson result = ResultJson.error(HttpStatus.FORBIDDEN).message(accessDeniedException.getMessage());
                    writeResult(response, HttpStatus.FORBIDDEN, result);
                })
                // login 配置
                .and()
                .formLogin() // 使用 login form
                .successHandler((request, response, authentication) -> {
                    ResultJson result = ResultJson.ok().message("登录成功");
                    result.data("user", authentication.getPrincipal());
                    if (isJwt()) {
                        // 生成 token
                        String token = JwtTokenUtil.generateJwtToken(authentication);
                        result.data("jwt", token);
                    }
                    writeResult(response, HttpStatus.OK, result);
                })
                .failureHandler((request, response, e) -> {
                    // 登录失败
                    ResultJson result = ResultJson.error(HttpStatus.UNAUTHORIZED).message(e.getMessage());
                    writeResult(response, HttpStatus.UNAUTHORIZED, result);
                })
                .and()
                .logout()
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessHandler((request, response, authentication) -> {
                    // 退出成功
                    ResultJson result = ResultJson.ok().message("退出成功");
                    writeResult(response, HttpStatus.OK, result);
                    // to do 如果是jwt，要把jwt失效
                });

        if (isJwt()) {
            // 使用jwt，不使用 session
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            http.addFilterBefore(new AuthTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        }
        return http.build();
    }

    private boolean isJwt() {
        return "jwt".equalsIgnoreCase(securityType);
    }

    private void writeResult(HttpServletResponse response, HttpStatus status, ResultJson resultJson) throws IOException {
        response.setStatus(status.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(resultJson);
        PrintWriter out = response.getWriter();
        out.write(json);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return passwordEncoder;
    }

}
